import gov.nih.mipav.model.structures.ModelImage;
import gov.nih.mipav.view.ViewUserInterface;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;

import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.TitledBorder;

/**
 * This manager contains all the information related to the images to be
 * segmented.
 * 
 * @author marco.zanger
 */
public class ImageManager extends ListModule implements Drawable,
    MethodRelated, ActionListener {

  /**
   * The combo from where to select any of the images that were loaded into the
   * program.
   */
  private DefaultComboBoxModel loadedImagesModel;

  private static ImageManager instance;

  /**
   * The constructor initialize the model for the combo of loaded images, the
   * list of added images and the list of selected images.
   */
  private ImageManager() {
    super();
    loadedImagesModel = new DefaultComboBoxModel();
  }

  public static ImageManager getInstance() {
    if (instance == null) {
      instance = new ImageManager();
    }
    return instance;
  }

  /**
   * Draws the contentPane with the buttons, comboBoxes and lists for the user
   * to operate.
   */
  public Component drawContentPane() {
    JButton addButton = new JButton("Add");
    addButton.setActionCommand("ADD");
    addButton.addActionListener(this);

    JButton removeButton = new JButton("Remove");
    removeButton.setActionCommand("REMOVE");
    removeButton.addActionListener(this);

    // Image pane
    JPanel imagesPane = new JPanel();
    imagesPane.setBorder(new TitledBorder("Image Selector"));

    imagesPane.setLayout(new BorderLayout(2, 4));
    JComboBox loadedImagesComboBox = new JComboBox(loadedImagesModel);
    loadedImagesComboBox.setMinimumSize(new Dimension(100, 20));
    imagesPane.add(loadedImagesComboBox, BorderLayout.NORTH);
    imagesPane.add(addButton, BorderLayout.WEST);
    imagesPane.add(removeButton, BorderLayout.EAST);

    JList selectedImagesList = this.getList();
    JScrollPane listScroller = new JScrollPane(selectedImagesList);
    listScroller.setPreferredSize(new Dimension(250, 80));
    imagesPane.add(listScroller, BorderLayout.SOUTH);

    return imagesPane;
  }

  /** I Think this pane should always be enable. */
  public void setEnable(boolean enable) {

  }

  /** This method updates the loaded images combo */
  protected void updateMe() {
    updateLoadedImages();
    removeNotLoadedImagesFromAddedList();
  }

  /**
   * After the loaded images is updated the system must check that all the
   * images in the added list are still loaded. If there is one that is loaded
   * no more, that should be erased from the list.
   */
  private void removeNotLoadedImagesFromAddedList() {
    for (int i = 0; i < this.getNumberOfObjects(); i++) {
      if (loadedImagesModel.getIndexOf(this.getObjectAt(i)) == -1)
        this.removeObjectAt(i);
    }
  }

  /**
   * If the update is triggered it might mean that the set of loaded images has
   * changed. So the combo box must be updates.
   */
  private void updateLoadedImages() {
    ViewUserInterface userInterface = ViewUserInterface.getReference();

    String selectedImage = (String) loadedImagesModel.getSelectedItem();
    loadedImagesModel.removeAllElements();
    loadedImagesModel.addElement("<NONE>");

    int numberOfImages = userInterface.getRegisteredImagesNum();
    if (numberOfImages > 0) {
      Enumeration<String> names = userInterface.getRegisteredImageNames();
      while (names.hasMoreElements()) {
        String temp = names.nextElement();
        loadedImagesModel.addElement(temp);
      }
    }

    if (loadedImagesModel.getIndexOf(selectedImage) >= 0) {
      loadedImagesModel.setSelectedItem(selectedImage);
    }
  }

  /** This method is triggered for the add action and the remove action. */
  public void actionPerformed(final ActionEvent event) {
    String command = event.getActionCommand();

    if (command.equals("ADD")) {
      this.addSelectedImage();
    } else if (command.equals("REMOVE")) {
      this.removeSelectedImages();
    }
  }

  /**
   * This method adds the selected image from the loaded images list to the
   * added list.
   */
  private void addSelectedImage() {
    String selected = (String) loadedImagesModel.getSelectedItem();
    if (!selected.equals("<NONE>") && !this.containsObject(selected)) {
      this.addObject(selected);
      VOIManager.getInstance().update();
    }
  }

  /**
   * This method removes the selected images from the added image list.
   */
  private void removeSelectedImages() {
    this.removeSelectedObjects();
    VOIManager.getInstance().update();
  }

  /**
   * Updates the data stored in the proxy.
   */
  public void updateProxy() {
    JSMethodProxy jsProxy = JSMethodProxy.getInstance();
    ViewUserInterface userInterface = ViewUserInterface.getReference();

    // Get the first ModelImage to set it's dimension
    if (this.getNumberOfObjects() > 0) {
      ModelImage model = userInterface.getRegisteredImageByName((String) this
          .getObjectAt(0));

      int pixels = model.getSize();

      jsProxy.setHeight(model.getHeight(model.getImageOrientation()));
      jsProxy.setWidth(model.getWidth(model.getImageOrientation()));
      jsProxy.setDepth(pixels / (jsProxy.getHeight() * jsProxy.getWidth()));
      jsProxy.setNumberOfImages(this.getNumberOfObjects());

      short[] images = new short[jsProxy.getNumberOfImages() * pixels];
      short[] temp = new short[pixels];

      try {
        for (int i = 0; i < this.getNumberOfObjects(); i++) {
          model = userInterface.getRegisteredImageByName((String) this
              .getObjectAt(0));
          model.exportData(0, pixels, temp);
          System.arraycopy(temp, 0, images, i * pixels, pixels);
        }

        jsProxy.setImages(images);
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

  }

  @SuppressWarnings("unchecked")
  public ArrayList<String> getImagesNames() {
    ArrayList<String> list = new ArrayList<String>();

    for (int i = 0; i < this.getNumberOfObjects(); i++) {
      list.add((String) this.getObjectAt(i));
    }
    return list;
  }

  public int getNumberOfImages() {
    return this.getNumberOfObjects();
  }

  /**
   * Only needs to be update in case the loaded images changed
   */
  @Override
  public boolean needsToBeUpdated() {
    boolean update = false;

    ViewUserInterface userInterface = ViewUserInterface.getReference();
    Enumeration<String> currentNames = userInterface.getRegisteredImageNames();

    int currentNamesLength = 1;
    while (currentNames.hasMoreElements()) {
      currentNamesLength++;
      String temp = currentNames.nextElement();
      update = update || (loadedImagesModel.getIndexOf(temp) == -1);
    }

    update = update || (loadedImagesModel.getSize() != currentNamesLength);
    return update;
  }

  @Override
  protected void modifyObject() {
  }

  @Override
  protected void selectObject() {
  }

}
